import {
  ref, reactive, getCurrentInstance, toRefs,
} from 'vue'
import { ethers } from "ethers"

import Web3, { utils } from 'web3'
import Web3Modal from 'web3modal'
import { getChainData } from '@/web3/tools'
import { providerOptions } from '@/web3/config'
import { RND, ControllerV1, BoyToken, HorseToken } from '../components/mainnet.json'
import { Toast, Notify } from 'vant'
let stakingRND = ethers.BigNumber.from('450000000000000000000000000')
const isAllowed = ref(false)

const INITIAL_STATE = {
  web3: null,
  provider: null,
  userAddress: '',
  connected: false,
  chainId: 1, // 设置网络地址
  networkId: 1,
  canMintBoy: 8,
  canMintHorse: 10,
  loadingMint: false,
  isChange: false
}
export default function UseWallet() {
  const { ctx: _this } = getCurrentInstance()

  const walletObj = reactive({ ...INITIAL_STATE })
  const fetching = ref(false)
  const assets = ref(0)
  //https://github.com/Web3Modal/web3modal#web3modal
  const web3Modal = new Web3Modal({
    theme: 'dark',
    network: getChainData(walletObj.chainId).network,
    cacheProvider: true,
    providerOptions,
  })
  // methods wallte.js
  const resetApp = async () => {
    const { web3 } = walletObj
    if (web3 && web3.currentProvider && web3.currentProvider.close) {
      await web3.currentProvider.close()
    }

    web3Modal.clearCachedProvider()
    assets.value = 0
    Object.keys(INITIAL_STATE).forEach((e) => {
      walletObj[e] = INITIAL_STATE[e]
    })
    _this.$forceUpdate()
  }
  const getUserBalance = () => walletObj.web3.eth
    .getBalance(walletObj.userAddress)
    .then((res) => (res ? utils.fromWei(res.toString(), 'ether') : 0))

  const getAccountAssets = async () => {
    fetching.value = true
    // get account balances

    assets.value = await getUserBalance()
  }
  const subscribeProvider = async (provider) => {
    if (!provider.on) {
      return
    }
    provider.on('close', () => resetApp())
    provider.on('accountsChanged', async (accounts) => {
      // eslint-disable-next-line prefer-destructuring
      walletObj.userAddress = accounts[0]
      walletObj.isChange = !walletObj.isChange
      await getAccountAssets()
      canMintAmount(accounts[0])

    })
    provider.on('chainChanged', async (chainId) => {
      const networkId = await walletObj?.web3?.eth?.net.getId()
      walletObj.chainId = chainId
      walletObj.networkId = networkId
      await getAccountAssets()
    })
  }

  const onConnect = async () => {

    const provider = await web3Modal.connect()

    await subscribeProvider(provider)

    const web3 = new Web3(provider)
    const accounts = await web3.eth.getAccounts()

    const address = accounts[0]

    const networkId = await web3.eth.net.getId()

    const chainId = await web3.eth.getChainId() // 坑逼 注意版本 chainId
    if (INITIAL_STATE.chainId !== chainId) {
      Toast(_this.$t('SwitchRinkeby'))
      return
    }
    walletObj.web3 = web3
    walletObj.provider = provider
    walletObj.connected = true
    walletObj.userAddress = address
    walletObj.chainId = chainId
    walletObj.networkId = networkId
    await getAccountAssets()
  }
  const onMintTest = async (count, address) => {
    // 合约函数
    const mintCount = ethers.BigNumber.from(count)
    const recommenderAddress = address
    const provider1 = new ethers.providers.Web3Provider(walletObj.provider)
    const signer = provider1.getSigner()
    const selectedAccount = walletObj.userAddress
    const controllerV1 = new ethers.Contract(ControllerV1.address, ControllerV1.abi, signer)
    const rnd = new ethers.Contract(RND.address, RND.abi, signer)
    const boyToken = new ethers.Contract(BoyToken.address, BoyToken.abi, signer)
    const balanceOf = await boyToken.balanceOf(selectedAccount)

    const maxMintCount = await controllerV1.getMintLimit(0)
    const canMitCount = maxMintCount - balanceOf;
    // 判断能mint数
    if (mintCount > canMitCount) {
      Notify({ type: 'warning', message: _this.$t('LimitExceeded') });
      walletObj.loadingMint = false
      return;
    }

    // 判断可freemint 个数
    const freeMintCount = await controllerV1.getFreeMintCount(selectedAccount);

    if (freeMintCount < mintCount) {
      const total = mintCount.mul(stakingRND);
      const balance = await rnd.balanceOf(selectedAccount);
      if (total.gt(balance)) {
        Notify({ type: 'warning', message: _this.$t('RechargeRND')});
        walletObj.loadingMint = false
        return;
      }
      try {
        const allowanceAmount = await rnd.allowance(selectedAccount, controllerV1.address);
        if (total.gt(allowanceAmount)) {
          let popCount = 0;
          rnd.on("Approval", (owner, spender, value) => {
            if (popCount == 0 && owner == selectedAccount && spender == controllerV1.address && value._hex == total._hex) {
              popCount++;
              const mintPromise = controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
                const scanLink = getChainData(walletObj.chainId).scan+ "/tx/" + res2.hash;
                Toast(_this.$t('MintSucceeded'))
                walletObj.loadingMint = false
                canMintAmount()
              });
            }
          });
          const res1 = await rnd.approve(controllerV1.address, total);
          console.log('res1', res1);
          if (!res1 || !res1.hash) {
            Notify({ type: 'warning', message: _this.$t('ApproveFail') });
            walletObj.loadingMint = false
          }
        } else {
          controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
            console.log("res2", res2);
            const scanLink = getChainData(walletObj.chainId).scan+ "/tx/" + res2.hash;
            console.log('scanLink===')
            console.log(scanLink)
            canMintAmount()
            Toast(_this.$t('MintSucceeded'))
            walletObj.loadingMint = false
          });
        }
      }
      catch (e) {
        walletObj.loadingMint = false
        Notify({ type: 'warning', message: _this.$t('MintFail') });
      }
    } else {
      controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
        console.log("res2", res2);
        const scanLink = getChainData(walletObj.chainId).scan+ "/tx/" + res2.hash;
        console.log('scanLink===')
        console.log(scanLink)
        Toast(_this.$t('FreeMint'))
        walletObj.loadingMint = false
      });
    }
  }
  // 是否授权过
  const transferByExternal = () => {
    return new Promise(async (resolve, reject) => {
      try {
        const { web3 } = walletObj
        const selectedAddress = await web3.eth.getAccounts()
        const provider1 = new ethers.providers.Web3Provider(walletObj.provider)
        const signer = provider1.getSigner()
        const controllerV2 = new ethers.Contract(RND.address, RND.abi, signer)
        const result = await controllerV2.allowance(selectedAddress[0], ControllerV1.address)
        resolve(result)
      } catch {
        reject(false)
      }
    })
  }
  const onMintTest2 = async (count) => {
    try {
      const provider1 = new ethers.providers.Web3Provider(walletObj.provider)
      const signer = provider1.getSigner()
      const selectedAccount = walletObj.userAddress
      const controllerV1 = new ethers.Contract(ControllerV1.address, ControllerV1.abi, signer)
      const mintCount = ethers.BigNumber.from(count);
      const price = await controllerV1.getPrice(1);
      const payAmount = mintCount.mul(price);
      const provider = ethers.getDefaultProvider();
      const gasPrice = await provider.getGasPrice();
      const overrides = {
        value: payAmount,
      };
      const res = await controllerV1.mintHorseAndArms(mintCount, overrides);
      if (res && res.hash) {
        Toast(_this.$t('MintSucceeded'))
      } else {
        Toast(_this.$t('MintFail'))
      }
      walletObj.loadingMint = false
      const scanLink = getChainData(walletObj.chainId).scan + "/tx/" + res.hash
    } catch (err){
      const { code,error } = err
      if (error && error.code === -32000) {
        Toast(_this.$t('MintFailToBanlce'))
        walletObj.loadingMint = false
      } else {
        Toast(_this.$t('MintFail'))
        walletObj.loadingMint = false
      }
      
    }
  }
  const canMintAmount = async (addresss) => {
    const provider1 = new ethers.providers.Web3Provider(walletObj.provider)
    const signer = provider1.getSigner()
    const selectedAccount = addresss ? addresss : walletObj.userAddress
    const controllerV1 = new ethers.Contract(ControllerV1.address, ControllerV1.abi, signer)
    const rnd = new ethers.Contract(RND.address, RND.abi, signer)
    const boyToken = new ethers.Contract(BoyToken.address, BoyToken.abi, signer)
    const balanceOf = await boyToken.balanceOf(selectedAccount)
    const horseToken = new ethers.Contract(HorseToken.address, HorseToken.abi, signer);

    const maxMintCount1 = await controllerV1.getMintLimit(0);
    const balanceOf1 = await boyToken.balanceOf(selectedAccount);
    const canMintCount1 = maxMintCount1 - balanceOf1;
    const maxMintCount2 = await controllerV1.getMintLimit(1);
    const balanceOf2 = await horseToken.balanceOf(selectedAccount);
    const canMintCount2 = maxMintCount2 - balanceOf2;
    walletObj.canMintBoy = canMintCount1
    walletObj.canMintHorse = canMintCount2
    _this.$forceUpdate()
  }

//  const checkGas = (newContract, address) => {
//     return new Promise((resolve, reject) => {
//       newContract.estimateGas({from: address}).then(res => {
//         console.log(res)
//         resolve(true)
//       }).catch(err => {
//         console.log(err)
//         resolve(false)
//       }).catch((err) => {
//         reject(false)
//       })
//     })
//   }

  return {
    ...toRefs(walletObj),
    fetching,
    assets,
    resetApp,
    getAccountAssets,
    //
    web3Modal,
    // methods
    onConnect,
    onMintTest,
    onMintTest2,
    transferByExternal,
    canMintAmount,
  }
}
